// $Id$
#include <stdio.h>
#include <string.h>

////////////////////////////////////////////////////////////////////////////////
// Macro definition
////////////////////////////////////////////////////////////////////////////////
#define BUFFER_SIZE_MESSAGE	2048
#define BUFFER_SIZE_PRIORITY	16
#define BUFFER_SIZE_VERSION	16
#define BUFFER_SIZE_TIMESTAMP	64
#define BUFFER_SIZE_HOSTNAME	64
#define BUFFER_SIZE_APPNAME	64
#define BUFFER_SIZE_PROCID	16
#define BUFFER_SIZE_MSGID	16
#define BUFFER_SIZE_SEGMENT	1024

////////////////////////////////////////////////////////////////////////////////
// Local variables
////////////////////////////////////////////////////////////////////////////////
static char const rcsid[] = "$Id$";

////////////////////////////////////////////////////////////////////////////////
// Function implementation
////////////////////////////////////////////////////////////////////////////////
void parse_rfc3164_raw(const char *buf, int len)
{
  char priority[BUFFER_SIZE_PRIORITY];
  char timestamp[BUFFER_SIZE_TIMESTAMP];
  char hostname[BUFFER_SIZE_HOSTNAME];
  char appname[BUFFER_SIZE_APPNAME];
  char message[BUFFER_SIZE_MESSAGE];

  //-----------------------------------------------------------------------------
  // XXX: Prepare related buffer
  //-----------------------------------------------------------------------------
  memset(priority, 0, sizeof(priority));
  memset(timestamp, 0, sizeof(timestamp));
  memset(hostname, 0, sizeof(hostname));
  memset(appname, 0, sizeof(appname));
  memset(message, 0, sizeof(message));

  //-----------------------------------------------------------------------------
  // XXX: Dump original data
  //-----------------------------------------------------------------------------
  fprintf(stdout, "Original: %s\n", buf);

  int offset = 0;
  int i;

  //-----------------------------------------------------------------------------
  // XXX: Calculate priority
  //-----------------------------------------------------------------------------
  // Refer to: http://tools.ietf.org/html/rfc3164#section-4.1.1
  //
  // The PRI part MUST have three, four, or five characters and will be
  // bound with angle brackets as the first and last characters.  The PRI
  // part starts with a leading "<" ('less-than' character), followed by a
  // number, which is followed by a ">" ('greater-than' character). 
  //
  // The code set used in this part MUST be seven-bit ASCII in an eight-bit
  // field as described in RFC 2234 [2].
  //
  //-----------------------------------------------------------------------------
  //           Table 1.  syslog Message Facilities
  //-----------------------------------------------------------------------------
  //       Numerical             Facility
  //          Code
  //           0             kernel messages
  //           1             user-level messages
  //           2             mail system
  //           3             system daemons
  //           4             security/authorization messages (note 1)
  //           5             messages generated internally by syslogd
  //           6             line printer subsystem
  //           7             network news subsystem
  //           8             UUCP subsystem
  //           9             clock daemon (note 2)
  //          10             security/authorization messages (note 1)
  //          11             FTP daemon
  //          12             NTP subsystem
  //          13             log audit (note 1)
  //          14             log alert (note 1)
  //          15             clock daemon (note 2)
  //          16             local use 0  (local0)
  //          17             local use 1  (local1)
  //          18             local use 2  (local2)
  //          19             local use 3  (local3)
  //          20             local use 4  (local4)
  //          21             local use 5  (local5)
  //          22             local use 6  (local6)
  //          23             local use 7  (local7)
  //-----------------------------------------------------------------------------
  //           Table 2. syslog Message Severities
  //-----------------------------------------------------------------------------
  //
  //        Numerical         Severity
  //          Code
  //           0       Emergency: system is unusable
  //           1       Alert: action must be taken immediately
  //           2       Critical: critical conditions
  //           3       Error: error conditions
  //           4       Warning: warning conditions
  //           5       Notice: normal but significant condition
  //           6       Informational: informational messages
  //           7       Debug: debug-level messages
  //-----------------------------------------------------------------------------
  if (buf[0] == '<')
  {
    offset++;

    for (i = 0; buf[offset] != '>'; i++)
    {
      priority[i] = buf[offset];
      offset++;
    }

    offset++;
  }

  fprintf(stdout, "offset = %d, rest: %s\n", offset, buf + offset);

  //-----------------------------------------------------------------------------
  // XXX: Calculate RFC header
  //
  // The HEADER contains two fields called the TIMESTAMP and the HOSTNAME.
  //
  // The TIMESTAMP will immediately follow the trailing ">" from the PRI
  // part and single space characters MUST follow each of the TIMESTAMP
  // and HOSTNAME fields.  
  //
  // A single space character MUST follow the TIMESTAMP field.
  //
  // HOSTNAME will contain the hostname, as it knows itself.  If it does 
  // not have a hostname, then it will contain its own IP address. If a 
  // device has multiple IP addresses, it has usually been seen to use the
  // IP address from which the message is transmitted.  
  //
  // The TIMESTAMP field is the local time and is in the format of "Mmm dd
  // hh:mm:ss" (without the quote marks) 
  //-----------------------------------------------------------------------------
  if (buf[offset + 3] == ' ' && buf[offset + 6] == ' ' && buf[offset + 9] == ':' && buf[offset + 12] == ':' && buf[offset + 15] == ' ')
  {
    // XXX: find RFC header
    // XXX: fixed length 15
    memcpy(timestamp, buf + offset, 15);

    offset += 16;
    fprintf(stdout, "offset = %d, rest: %s\n", offset, buf + offset);

    // XXX: calculate hostname
    for (i = 0; buf[offset] != ' '; i++)
    {
      hostname[i] = buf[offset];
      offset++;
    }

    offset++;
    fprintf(stdout, "offset = %d, rest: %s\n", offset, buf + offset);

    // XXX: calculate application name
    for (i = 0; buf[offset] != ' '; i++)
    {
      appname[i] = buf[offset];
      offset++;
    }

    offset++;
    fprintf(stdout, "offset = %d, rest: %s\n", offset, buf + offset);
  }

  //-----------------------------------------------------------------------------
  // XXX: Calculate message
  //-----------------------------------------------------------------------------
  memcpy(message, buf + offset, len - offset);

  //-----------------------------------------------------------------------------
  // XXX: Dump result
  //-----------------------------------------------------------------------------
  fprintf(stdout, "Priority: %s.\n", priority);
  fprintf(stdout, "Timestamp: %s.\n", timestamp);
  fprintf(stdout, "Hostname: %s.\n", hostname);
  fprintf(stdout, "Process: %s.\n", appname);
  fprintf(stdout, "Message: %s.\n", message);
}

//------------------------------------------------------------------------------
// XXX: Function test
//------------------------------------------------------------------------------
// *IDENT-OFF*
static char *rfc3164_examples_data[] = 
{
  "<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8",
  "Use the BFG!",
  "<165>Aug 24 05:34:00 CST 1987 mymachine myproc[10]: %% It's time to make the do-nuts.  %%  Ingredients: Mix=OK, Jelly=OK # Devices: Mixer=OK, Jelly_Injector=OK, Frier=OK # Transport: Conveyer1=OK, Conveyer2=OK # %%",
  "<0>1990 Oct 22 10:52:01 TZ-6 scapegoat.dmz.example.org 10.1.2.3 sched[0]: That's All Folks!",
  "<0>Oct 22 10:52:12 scapegoat 1990 Oct 22 10:52:01 TZ-6 scapegoat.dmz.example.org 10.1.2.3 sched[0]: That's All Folks!",
  NULL
};
// *IDENT-ON*

//------------------------------------------------------------------------------
// XXX: Function test
//------------------------------------------------------------------------------
void rfc3164_function_test(void)
{
  char **p = rfc3164_examples_data;

  while (p && *p)
  {
    fprintf(stdout, "%s\n", *p);
    parse_rfc3164_raw(*p, strlen(*p));
    p++;
  }
}
